package com.zlmediakit.zlmwebrtc;

import android.content.Context;
import android.util.Log;

import org.webrtc.AudioSource;
import org.webrtc.AudioTrack;
import org.webrtc.Camera1Enumerator;
import org.webrtc.Camera2Enumerator;
import org.webrtc.CameraEnumerator;
import org.webrtc.DataChannel;
import org.webrtc.DefaultVideoDecoderFactory;
import org.webrtc.DefaultVideoEncoderFactory;
import org.webrtc.EglBase;
import org.webrtc.IceCandidate;
import org.webrtc.MediaConstraints;
import org.webrtc.MediaStream;
import org.webrtc.MediaStreamTrack;
import org.webrtc.PeerConnection;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.RtpReceiver;
import org.webrtc.RtpTransceiver;
import org.webrtc.SdpObserver;
import org.webrtc.SessionDescription;
import org.webrtc.SurfaceTextureHelper;
import org.webrtc.SurfaceViewRenderer;
import org.webrtc.VideoCapturer;
import org.webrtc.VideoDecoderFactory;
import org.webrtc.VideoEncoderFactory;
import org.webrtc.VideoSource;
import org.webrtc.VideoTrack;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Kyle
 * @explain
 * @creat 2022/6/26 0:50
 */
public class WebRtcWrapper {
    private String TAG="WebRtcWrapper";
    private EglBase.Context eglBaseContext;
    private PeerConnectionFactory peerConnectionFactory;

    private AudioTrack audioTrack;
    private VideoTrack videoTrack;
    private VideoCapturer videoCapturer;
    private SurfaceTextureHelper surfaceTextureHelper;
    private Context context;

    public WebRtcWrapper(Context context,boolean recvOnly){
        this.context=context;
        initWebRTC();
        if(!recvOnly){
            initLocalMediaSouce();
        }
    }


    private void initWebRTC(){
        eglBaseContext=EglBase.create().getEglBaseContext();;
        peerConnectionFactory=createPeerConnectionFactory();
    }

    public void initLocalMediaSouce(){
        AudioSource audioSource = peerConnectionFactory.createAudioSource(new MediaConstraints());
        audioTrack = peerConnectionFactory.createAudioTrack("101", audioSource);

        videoCapturer = createVideoCapturer();
        surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", eglBaseContext);
        VideoSource videoSource = peerConnectionFactory.createVideoSource(videoCapturer.isScreencast());
        videoCapturer.initialize(surfaceTextureHelper, context, videoSource.getCapturerObserver());
        videoCapturer.startCapture(1280, 720, 30);

        videoTrack = peerConnectionFactory.createVideoTrack("102", videoSource);
    }

    private PeerConnectionFactory createPeerConnectionFactory() {
        eglBaseContext=EglBase.create().getEglBaseContext();
        final VideoEncoderFactory encoderFactory;
        final VideoDecoderFactory decoderFactory;

        encoderFactory = new DefaultVideoEncoderFactory(
                eglBaseContext, false /* enableIntelVp8Encoder */, true);
        decoderFactory = new DefaultVideoDecoderFactory(eglBaseContext);

        PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions.builder(context)
                .setEnableInternalTracer(true)
                .createInitializationOptions());

        PeerConnectionFactory.Builder builder = PeerConnectionFactory.builder()
                .setVideoEncoderFactory(encoderFactory)
                .setVideoDecoderFactory(decoderFactory);
        builder.setOptions(null);

        return builder.createPeerConnectionFactory();
    }

    private VideoCapturer createVideoCapturer() {
        if (Camera2Enumerator.isSupported(context)) {
            return createCameraCapturer(new Camera2Enumerator(context));
        } else {
            return createCameraCapturer(new Camera1Enumerator(true));
        }
    }

    private VideoCapturer createCameraCapturer(CameraEnumerator enumerator) {
        final String[] deviceNames = enumerator.getDeviceNames();

        // First, try to find front facing camera
        Log.d(TAG, "Looking for front facing cameras.");
        for (String deviceName : deviceNames) {
            if (enumerator.isFrontFacing(deviceName)) {
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }

        // Front facing camera not found, try something else
        Log.d(TAG, "Looking for other cameras.");
        for (String deviceName : deviceNames) {
            if (!enumerator.isFrontFacing(deviceName)) {
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }
        return null;
    }

    public PeerConnectionFactory getPeerConnectionFactory() {
        return peerConnectionFactory;
    }

    public EglBase.Context getEglBaseContext() {
        return eglBaseContext;
    }

    public PeerConnection getLocalMediaPeerConnection(PeerConnection.Observer peerConnectionObserver){
        List<PeerConnection.IceServer> iceServerList = new ArrayList<>();
        PeerConnection.RTCConfiguration rtcConfiguration=new PeerConnection.RTCConfiguration(iceServerList);
        rtcConfiguration.sdpSemantics= PeerConnection.SdpSemantics.UNIFIED_PLAN;
        PeerConnection peerConnection = getPeerConnectionFactory().createPeerConnection(rtcConfiguration, peerConnectionObserver);
        peerConnection.addTransceiver(MediaStreamTrack.MediaType.MEDIA_TYPE_AUDIO, new RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.SEND_ONLY));
        peerConnection.addTransceiver(MediaStreamTrack.MediaType.MEDIA_TYPE_VIDEO, new RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.SEND_ONLY));
        peerConnection.addTrack(audioTrack);
        peerConnection.addTrack(videoTrack);
        return peerConnection;
    }

    public PeerConnection createRemotePeerConnection(PeerConnection.Observer peerConnectionObserver){
        List<PeerConnection.IceServer> iceServerList = new ArrayList<>();
        PeerConnection.RTCConfiguration rtcConfiguration=new PeerConnection.RTCConfiguration(iceServerList);
        rtcConfiguration.sdpSemantics= PeerConnection.SdpSemantics.UNIFIED_PLAN;
        PeerConnection peerConnection = getPeerConnectionFactory().createPeerConnection(rtcConfiguration, peerConnectionObserver);
        peerConnection.addTransceiver(MediaStreamTrack.MediaType.MEDIA_TYPE_AUDIO, new RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.RECV_ONLY));
        peerConnection.addTransceiver(MediaStreamTrack.MediaType.MEDIA_TYPE_VIDEO, new RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.RECV_ONLY));
        return peerConnection;
    }

    public void setLocalMediaView(SurfaceViewRenderer surfaceViewRendererLocal){
        videoTrack.addSink(surfaceViewRendererLocal);
    }

    public static class SimpleSdpObserver implements SdpObserver{
        private String FROM_TAG;
        public SimpleSdpObserver(String FROM_TAG){
            this.FROM_TAG=getClass().getSimpleName()+"-"+FROM_TAG;
        }
        @Override
        public void onCreateSuccess(SessionDescription sessionDescription) {
            Log.d(FROM_TAG,"onCreateSuccess:"+sessionDescription);
        }

        @Override
        public void onSetSuccess() {
            Log.d(FROM_TAG,"onSetSuccess");
        }

        @Override
        public void onCreateFailure(String s) {
            Log.d(FROM_TAG,"onCreateFailure:"+s);
        }

        @Override
        public void onSetFailure(String s) {
            Log.d(FROM_TAG,"onSetFailure:"+s);
        }
    }

    public static class SimplePeerConnectionObserver implements PeerConnection.Observer {
        private String FROM_TAG;
        public SimplePeerConnectionObserver(String FROM_TAG){
            this.FROM_TAG=getClass().getSimpleName()+"-"+FROM_TAG;
        }
        @Override
        public void onSignalingChange(PeerConnection.SignalingState signalingState) {
            Log.d(FROM_TAG,"onSignalingChange:"+signalingState.toString());
        }

        @Override
        public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
            Log.d(FROM_TAG,"onIceConnectionChange:"+iceConnectionState.toString());
        }

        @Override
        public void onIceConnectionReceivingChange(boolean b) {
            Log.d(FROM_TAG,"onIceConnectionReceivingChange:"+b);
        }

        @Override
        public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
            Log.d(FROM_TAG,"onIceGatheringChange:"+iceGatheringState.toString());
        }

        @Override
        public void onIceCandidate(IceCandidate iceCandidate) {
            Log.d(FROM_TAG,"onIceCandidate:"+iceCandidate.toString());
        }

        @Override
        public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) {
            Log.d(FROM_TAG,"onIceCandidatesRemoved:"+ Arrays.toString(iceCandidates));
        }

        @Override
        public void onAddStream(MediaStream mediaStream) {
            Log.d(FROM_TAG,"onAddStream:"+ mediaStream.toString());
        }

        @Override
        public void onRemoveStream(MediaStream mediaStream) {
            Log.d(FROM_TAG,"onRemoveStream:"+ mediaStream.toString());
        }

        @Override
        public void onDataChannel(DataChannel dataChannel) {
            Log.d(FROM_TAG,"onDataChannel:"+ dataChannel.toString());
        }

        @Override
        public void onRenegotiationNeeded() {
            Log.d(FROM_TAG,"onRenegotiationNeeded");
        }

        @Override
        public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) {

        }
    }

    public void release(){
        if(videoTrack!=null){
            videoTrack.dispose();
        }

        if(audioTrack!=null){
            audioTrack.dispose();
        }

        if(videoCapturer!=null){
            try {
                videoCapturer.stopCapture();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            videoCapturer.dispose();
        }

        if(surfaceTextureHelper!=null){
            surfaceTextureHelper.dispose();
        }

        if(peerConnectionFactory!=null){
            peerConnectionFactory.dispose();
        }
    }
}
